package org.jvm.nativemethod.methods.java.lang;

import org.jvm.nativemethod.*;
import org.jvm.rtda.Object;
import org.jvm.rtda.StackTraceElement;
import org.jvm.rtda.heap.Klass;
import org.jvm.rtda.heap.classLoader.KlassLoaderRegister;
import org.jvm.rtda.thread.Frame;

import java.util.*;

/**
 * @author 海燕
 * @date 2023/2/26
 */
public class ThrowableNativeMethod extends NativeMethodRegister {

    public static void init() throws NoSuchMethodException {
        register("java/lang/Throwable", "fillInStackTrace", "(I)Ljava/lang/Throwable;", ThrowableNativeMethod.class.getDeclaredMethod("fillInStackTrace", Frame.class));
    }

    /**
     * 填充线程栈信息
     *
     * @param frame
     */
    public static void fillInStackTrace(Frame frame) {
        Object ref = frame.getLocalVars().getRef(0);
        frame.getOperandStack().pushRef(ref);
        Stack<Frame> frameStack = frame.getThread().getStack().getStack();
        Klass throwableClass = KlassLoaderRegister.getBootKlassLoader().loadKlass("java/lang/Throwable");
        List<org.jvm.rtda.StackTraceElement> stackTraceElementList = new ArrayList<>();
        for (int i = frameStack.size() - 1; i >= 0; i--) {
            Frame f = frameStack.get(i);
            //栈顶的若干帧可能正在构造Throwable实例，这几帧跳过
            if (f.getMethod().getKlass().isSubClassOf(throwableClass)
                    || f.getMethod().getKlass() == throwableClass
                    //跳过间隙帧
                    || f.getMethod().getKlass() == NativeMethodUtil.shimClass) {
                continue;
            }
            org.jvm.rtda.StackTraceElement stackTraceElement = new org.jvm.rtda.StackTraceElement();
            //获取异常所在的类源文件名
            stackTraceElement.setFileName(f.getMethod().getKlass().getSourceFile());
            stackTraceElement.setClassName(f.getMethod().getKlass().getJavaName());
            stackTraceElement.setMethodName(f.getMethod().getName());
            //获取异常发生的源码行号
            stackTraceElement.setLineNumber(f.getMethod().getLineNumber(f.getNextPC() - 1));
            stackTraceElementList.add(stackTraceElement);
        }
        org.jvm.rtda.StackTraceElement[] stackTraceElementArray = stackTraceElementList.toArray(new StackTraceElement[0]);
        ref.setExtra(stackTraceElementArray);
    }
}
